跳到主要内容

Spring 中 BeanFactory 与 FactoryBean 的区别

直接区别

直面意思:Bean工厂、工厂Bean

1、BeanFactory:以 Factory 结尾,表示它是一个工厂类(接口),用于管理 Bean 的一个工厂。在 Spring 中,BeanFactory 是 IOC 容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

2、FactoryBean:以 Bean 结尾,表示它是一个 Bean,不同于普通 Bean 的是:它是实现了 FactoryBean<T> 接口的 Bean,通过该 Bean 的 ID 从 BeanFactory 中获取的实际上是 FactoryBean 的 getObject() 返回的对象,而不是 FactoryBean 本身,如果要获取 FactoryBean 对象,请在 id 前面加一个 & 符号来获取。

BeanFactory 是什么?

BeanFactory,以 Factory 结尾,表示它是一个工厂类(接口),用于管理 Bean 的一个工厂。在 Spring 中,BeanFactory 是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

BeanFactory 是一个接口,它是 Spring 中工厂的顶层规范,是 SpringIoc 容器的核心接口,它定义了 getBean()containsBean() 等管理 Bean 的通用方法。

Spring 的容器都是它的具体实现如:

  • DefaultListableBeanFactory
  • XmlBeanFactory
  • ApplicationContext

这些实现类又从不同的维度分别有不同的扩展。

例如 XmlBeanFactory 类将持有此 XML 配置元数据,并用它来构建一个完全可配置的系统或应用,如下实例化容器

Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ClassPathResource resource = new ClassPathResource("beans.xml");

BeanFactory factory = new XmlBeanFactory(resource);

ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});

BeanFactory factory = (BeanFactory) context;

BeanFactory 接口解读

下面介绍一下这个核心接口的内容

public interface BeanFactory {

// 对 FactoryBean 的转义定义
// 因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象,
// 如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";

// 根据 bean 的名字,获取在 IOC 容器中得到 bean 实例
Object getBean(String name) throws BeansException;

//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

<T> T getBean(Class<T> requiredType) throws BeansException;

<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);

//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

//得到bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;

//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}

这个 BeanFactory 使用场景

1、从 Ioc 容器中获取 Bean(byName or byType)

2、检索 Ioc 容器中是否包含指定的 Bean

3、判断 Bean 是否为单例

FactoryBean 是什么?

一般情况下,Spring 通过反射机制利用 <bean> 的 class 属性指定实现类实例化 Bean,在某些情况下,实例化 Bean 过程比较复杂,如果按照传统的方式,则需要在 <bean> 中提供大量的配置信息。

因为配置方式的灵活性是受限的,这时采用编码的方式反而可能更快,这样就违背了 Spring 的意图了,所以 Spring 为此提供了一个 FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 Bean 的逻辑。

第三方框架要继承进 Spring,往往就是通过实现 FactoryBean 来集成的。比如 MyBatis 的 SqlSessionFactoryBean、RedisRepositoryFactoryBean、EhCacheManagerFactoryBean 等等

实现了这个 FactoryBean 接口的类,它只是一个 Bean,它是一个生产或修饰对象生成的工厂 Bean,类似于设计模式中的工厂模式和装饰器模式。

它能生产一个对象,且不仅仅限于它自身,它能返回任何 Bean 的实例。

补充:FactoryBean 接口对于 Spring 框架来说占用重要的地位,Spring自身就提供了70多个 FactoryBean 的实现。

这个接口如下所示:

public interface FactoryBean<T> {

//从工厂中获取bean
@Nullable
T getObject() throws Exception;

//获取Bean工厂创建的对象的类型
@Nullable
Class<?> getObjectType();

//Bean工厂创建的对象是否是单例模式
default boolean isSingleton() {
return true;
}
}

从 FactoryBean 定义的接口可以看出,FactoryBean 表现的是一个工厂的职责。 即一个 Bean A 如果实现了 FactoryBean 接口,那么 A 就变成了一个工厂,根据 A 的名称获取到的实际上是工厂调用 getObject() 返回的对象,而不是 A 本身,如果要获取工厂 A 自身的实例,那么需要在名称前面加上 '&' 符号。

  • getObject('name') 返回工厂中的实例
  • getObject('&name') 返回工厂本身的实例

注意:通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。

补充:在 Bean 循环依赖三级缓存那篇笔记中可以看到:

/** 三级级缓存:存放 bean 工厂对象,用于解决循环依赖 */
private final Map<String, ObjectFactory<?>> singletonFactories =
new HashMap<>(16);

它是做第三级缓存的

FactoryBean 使用场景

说了这么多,为什么要有 FactoryBean 这个东西呢,有什么具体的作用吗?

FactoryBean 在 Spring 中最为典型的一个应用就是用来 创建 AOP 的代理对象

我们知道 AOP 实际上是 Spring 在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。

更形象地说,AOP 代理对象通过 Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在 Spring 中就是——ProxyFactoryBean。

所以,FactoryBean 为我们实例化 Bean 提供了一个更为灵活的方式,我们可以通过 FactoryBean 创建出更为复杂的 Bean 实例。

使用示例

注意:一般 Spring 内置的 FactoryBean 就足够使用了,这里只是为了演示如何自定义 FactoryBean

先定义一个 Bean 实现 FactoryBean 接口

@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = "通过构造方法初始化实例";
}

@Override
public Object getObject() throws Exception {
// 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例。
//如return new Student()...
return new MyBean("通过FactoryBean.getObject()创建实例");
}

@Override
public Class<?> getObjectType() {
return MyBean.class;
}

public String getMessage() {
return message;
}
}

MyBean 实现了 FactoryBean 接口的两个方法,getObject() 是可以返回任何对象的实例的,这里测试就返回 MyBean 自身实例,且返回前给 message 字段赋值。同时在构造方法中也为 message 赋值。然后测试代码中先通过名称获取 Bean 实例,打印 message 的内容,再通过 &+名称 获取实例并打印 message 内容。

编写一个测试类,使用这个自定义的 FactoryBean

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
@Autowired
private ApplicationContext context;

@Test
public void test() {
MyBean myBean1 = (MyBean) context.getBean("myBean");
System.out.println("myBean1 = " + myBean1.getMessage());

MyBean myBean2 = (MyBean) context.getBean("&myBean");

System.out.println("myBean2 = " + myBean2.getMessage());
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}

打印结果

myBean1 = 通过FactoryBean.getObject()初始化实例
myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false

它们两个的区别

BeanFacotry 是 Spring 中比较原始的 Factory。如 XMLBeanFactory 就是一种典型的BeanFactory。

而常用的 ApplicationContext 接口,它就由 BeanFactory 接口派生而来,ApplicationContext 包含 BeanFactory 的所有功能,通常建议比 BeanFactory 优先

原始的 BeanFactory 无法支持 Spring 的许多插件,如 AOP 功能、Web 应用等。

即他们两个都是个工厂,但 FactoryBean 本质上还是一个 Bean,也归 BeanFactory 管理,而 BeanFactory 是 Spring 容器的顶层接口,FactoryBean 更类似于用户自定义的工厂接口。

总结:

BeanFactory 是接口,提供了 IOC 容器最基本的形式,给具体的 IOC 容器的实现提供了规范,

FactoryBean 也是接口,为 IOC 容器中 Bean 的实现提供了更加灵活的方式,FactoryBean在 IOC 容器的基础上给 Bean 的实现加上了一个简单工厂模式和装饰模式,我们可以在 getObject() 方法中灵活配置。

Reference

参考资料 【小家Spring】一文读懂Spring中的BeanFactory和FactoryBean(以及它和ObjectFactory的区别)的区别 参考资料 Spring中BeanFactory与FactoryBean的区别 参考资料 掌握Spring中的beanfactory与factorybean有什么好处?